using Godot;
using System;
using System.Collections.Generic;
using Common;
namespace DRE.TextWriter {
	abstract class Command : Common.Command {
		public abstract void Invoke(TextWriter sender, ComplexValue[] arguments);
	}
	class DelegateCommand : Command {
		public DelegateCommand(string id) {
			Id = id;
		}
		public override string Id { get; }
		public delegate void InvokeDelegate(TextWriter sender, ComplexValue[] arguments);
		public override void Invoke(TextWriter sender, ComplexValue[] arguments) {
			OnInvoke?.Invoke(sender, arguments);
		}
		public InvokeDelegate OnInvoke;
	}
	sealed class CommandManager : Common.CommandManager<Command> {
		public CommandManager() {
			Register(new DelegateCommand("Sleep") {
				Arguments = new Command.Argument[] {
					new Command.Argument("time",Common.Command.Argument.Type.Float)
				},
				OnInvoke = (sender, arguments) => {
					sender.Time = arguments[0].AsFloat();
				}
			});
			Register(new DelegateCommand("Speed") {
				Arguments = new Command.Argument[] {
					new Command.Argument("time",Common.Command.Argument.Type.Float)
				},
				OnInvoke = (sender, arguments) => {
					sender.Speed = arguments[0].AsFloat();
				}
			});
			Register(new DelegateCommand("Color") {
				Arguments = new Command.Argument[] {
					new Command.Argument("name",Common.Command.Argument.Type.String)
				},
				OnInvoke = (sender, arguments) => {
					sender.InstanceColor = arguments[0].AsString();
				}
			});
			Register(new DelegateCommand("Font") {
				Arguments = new Command.Argument[] {
					new Command.Argument("name",Common.Command.Argument.Type.String)
				},
				OnInvoke = (sender, arguments) => {
					sender.InstanceFont = arguments[0].AsString();
				}
			});
			Register(new DelegateCommand("Effect") {
				Arguments = new Command.Argument[] {
					new Command.Argument("name",Common.Command.Argument.Type.String)
				},
				OnInvoke = (sender, arguments) => {
					sender.InstanceEffect = arguments[0].AsString();
				}
			});
			Register(new DelegateCommand("Pause") {
				OnInvoke = (sender, arguments) => {
					sender.Paused = true;
				}
			});
			Register(new DelegateCommand("Scale") {
				Arguments = new Command.Argument[] {
					new Command.Argument("x",Common.Command.Argument.Type.Float),
					new Command.Argument("y",Common.Command.Argument.Type.Float)
				},
				OnInvoke = (sender, arguments) => {
					sender.InstanceScale = new Vector2(arguments[0].AsFloat(), arguments[1].AsFloat());
				}
			});
			Register(new DelegateCommand("Spacing") {
				Arguments = new Command.Argument[] {
					new Command.Argument("x",Common.Command.Argument.Type.Float),
					new Command.Argument("y",Common.Command.Argument.Type.Float)
				},
				OnInvoke = (sender, arguments) => {
					sender.InstanceSpacing = new Vector2(arguments[0].AsFloat(), arguments[1].AsFloat());
				}
			});
			Register(new DelegateCommand("End") {
				OnInvoke = (sender, arguments) => {
					sender.QueueFree();
				}
			});
			Register(new DelegateCommand("Clear") {
				OnInvoke = (sender, arguments) => {
					sender.Clear();
				}
			});
			Register(new DelegateCommand("Instant") {
				Arguments = new Command.Argument[] {
					new Command.Argument("enabled",Command.Argument.Type.Integer)
				},
				OnInvoke = (sender, arguments) => {
					sender.Instant = arguments[0].AsBool();
				}
			});
			Register(new DelegateCommand("VerticalAlign") {
				Arguments = new Command.Argument[] {
					new Command.Argument("valign",Command.Argument.Type.Integer)
				},
				OnInvoke = (sender, arguments) => {
					switch (arguments[0].AsInt()) {
						case 1:
							sender.VerticalAlign = VAlign.Center;
							break;
						case 2:
							sender.VerticalAlign = VAlign.Bottom;
							break;
						default:
							sender.VerticalAlign = VAlign.Top;
							break;
					}
				}
			});
			Register(new DelegateCommand("HorizontalAlign") {
				Arguments = new Command.Argument[] {
					new Command.Argument("halign",Command.Argument.Type.Integer)
				},
				OnInvoke = (sender, arguments) => {
					switch (arguments[0].AsInt()) {
						case 1:
							sender.HorizontalAlign = HAlign.Center;
							break;
						case 2:
							sender.HorizontalAlign = HAlign.Right;
							break;
						default:
							sender.HorizontalAlign = HAlign.Left;
							break;
					}
				}
			});
		}
		public void Execute(TextWriter sender, string command) {
			var rawCmd = Common.Command.Parse(command);
			string id = rawCmd.id;
			ComplexValue[] args = rawCmd.arguments;

			if (!IsExists(id)) {
				// TODO: Error report.
				return;
			}

			Command cmd = Get(id);
			var validated = Common.Command.ValidateArguments(cmd, args);
			if (validated.result != Common.Command.ArgumentValidateResult.OK) {
				// TODO: Error report.
				return;
			}

			ComplexValue[] formatted = Common.Command.FormatArguments(cmd, args);

			cmd.Invoke(sender, formatted);
		}
	}
}
